﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoDB.Driver;
using BlueLotus7.CDKey.Properties;
using MongoDB.Driver.Builders;

namespace BlueLotus7.CDKey
{
    public class CDKeyManager
    {
        private const string connectionString = "mongodb://cdkey:cdkey@ds041387.mongolab.com:41387/cdkey";
        private const string databaseName = "cdkey";
        private const string keyMapping = "_id";
        private static object locker = new object();
        private static object dbLocker = new object();

        public List<ApplicationName> ApplicationNameList
        {
            get;
            protected set;
        }

        public List<CDKeyObject> CDKeyObjects
        {
            get;
            protected set;
        }

        public CDKeyObject AddCDKey(CDKeyObject cdKeyObject)
        {
            CDKeyObject objtToSave = null;

            if (cdKeyObject != null)
            {
                lock (locker)
                {
                    foreach (var one in this.CDKeyObjects)
                    {
                        if (one.ApplicationName.ToLowerInvariant() == cdKeyObject.ApplicationName.ToLowerInvariant()
                            && one.KeyContent == cdKeyObject.KeyContent)
                        {
                            return one;
                        }
                    }
                    objtToSave = cdKeyObject;
                }
            }

            if (objtToSave != null
                && !string.IsNullOrWhiteSpace(objtToSave.ApplicationName)
                && !string.IsNullOrWhiteSpace(objtToSave.Version)
                && !string.IsNullOrWhiteSpace(objtToSave.KeyContent))
            {
                lock (dbLocker)
                {
                    GetMongoDBCollection().Insert<CDKeyObject>(objtToSave);
                }
                var name = cdKeyObject.ApplicationName.ToLowerInvariant();
                bool found = false;
                foreach (var one in this.ApplicationNameList)
                {
                    if (one.Key == name)
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    this.ApplicationNameList.Add(new ApplicationName(cdKeyObject.ApplicationName));
                }
            }

            return objtToSave;
        }

        public void RemoveCDKey(CDKeyObject cdKeyObject)
        {
            CDKeyObject obj = cdKeyObject == null ? null : GetObjectInList(cdKeyObject.Key);

            if (obj != null)
            {
                lock (locker)
                {
                    lock (dbLocker)
                    {
                        GetMongoDBCollection().FindAndRemove(Query.EQ(keyMapping, cdKeyObject.Key), SortBy.Null);
                    }

                    Init();
                }
            }
        }

        public void AddOrUpdateCDKey(CDKeyObject cdKeyObject)
        {
            CDKeyObject obj = cdKeyObject == null ? null : GetObjectInList(cdKeyObject.Key);

            if (obj != null)
            {
                UpdateCDKey(cdKeyObject);
            }
            else
            {
                AddCDKey(cdKeyObject);
            }
        }

        protected void UpdateCDKey(CDKeyObject cdKeyObject)
        {
            CDKeyObject obj = cdKeyObject == null ? null : GetObjectInList(cdKeyObject.Key);

            if (obj != null)
            {
                obj.IsVerified = cdKeyObject.IsVerified;
                obj.Comments = cdKeyObject.Comments;
                obj.IsOfficial = obj.IsOfficial ? true : cdKeyObject.IsOfficial;

                lock (dbLocker)
                {
                    GetMongoDBCollection().Update(Query.EQ(keyMapping, cdKeyObject.Key), Update.Replace<CDKeyObject>(obj));
                }
            }
        }

        public CDKeyObject GetObjectInList(Guid key)
        {
            CDKeyObject result = null;

            lock (locker)
            {
                foreach (var one in this.CDKeyObjects)
                {
                    if (one.Key == key)
                    {
                        result = one;
                    }
                }
            }

            return result;
        }

        public List<CDKeyObject> QueryCDKey(string applicationName, bool isVerified, bool isOfficial)
        {
            List<CDKeyObject> result = new List<CDKeyObject>();

            lock (locker)
            {
                applicationName = applicationName.ToLowerInvariant();

                foreach (var one in this.CDKeyObjects)
                {
                    if ((string.IsNullOrWhiteSpace(applicationName) || applicationName.ToLowerInvariant() == one.ApplicationName.ToLowerInvariant())
                        && (!isVerified || one.IsVerified)
                        && (!isOfficial || one.IsOfficial))
                    {
                        result.Add(one);
                    }
                }
                this.ApplicationNameList.SortByName();
                this.CDKeyObjects.SortByName();
            }

            return result;
        }

        public MongoCollection GetMongoDBCollection()
        {
            var server = MongoServer.Create(connectionString);
            var database = server.GetDatabase(databaseName);
            return database.GetCollection<CDKeyObject>(databaseName);
        }

        public CDKeyManager()
        {
            this.ApplicationNameList = new List<ApplicationName>();
            this.CDKeyObjects = new List<CDKeyObject>();
            Init();
        }

        protected void Init()
        {
            this.ApplicationNameList.Clear();
            this.CDKeyObjects.Clear();

            List<string> existingNames = new List<string>();

            var collection = GetMongoDBCollection();
            foreach (var one in collection.FindAllAs<CDKeyObject>().AsEnumerable())
            {
                this.CDKeyObjects.Add(one);
                var name = one.ApplicationName.ToLowerInvariant();
                if (!existingNames.Contains(name))
                {
                    this.ApplicationNameList.Add(new ApplicationName(one.ApplicationName));
                    existingNames.Add(name);
                }
            }

            this.ApplicationNameList.SortByName();
            this.CDKeyObjects.SortByName();
        }
    }
}
